#!/usr/bin/perl -w

use English;
use POSIX;
use Encode;
use Data::Dumper;	# used for debugging
use XML::Simple;

$infile = $ARGV[0];
$ref = {};	#hash with id => reforge
%statnames = (
	sta => "Stamina",
	int => "Intellect",
	agi => "Agility",
	str => "Strength",
	spi => "Spirit",
	hastertng => "Haste Rating",
	critstrkrtng => "Critical Strike Rating",
	hitrtng => "Hit Rating",
	mastrtng => "Mastery Rating",
	armor => "Armor",
	splpwr => "Spellpower",
);
open(LOAD, "< $infile")
	or die "Couldn't open $infile";
$xml = "";
while ($line = <LOAD>) { $xml .= $line; }
$char = XMLin($xml);
close(LOAD);

$items = $char->{gear};
#print Dumper($char);
$extrahit = 0;
foreach $item (keys(%{$char->{gear}->{item}})) {
	#print Dumper($char->{gear}->{item}->{$item});
	if ($char->{gear}->{item}->{$item}->{armorclass} == -7) { delete $char->{gear}->{item}->{$item}; } # remove tabard
	if ($char->{gear}->{item}->{$item}->{armorclass} == -8) { delete $char->{gear}->{item}->{$item}; } # remove shirt
	if (exists($char->{gear}->{item}->{$item}->{enchant}->{spi})) { $extrahit += $char->{gear}->{item}->{$item}->{enchant}->{spi}; }
	if (exists($char->{gear}->{item}->{$item}->{socket1}->{spi})) { $extrahit += $char->{gear}->{item}->{$item}->{socket1}->{spi}; }
	if (exists($char->{gear}->{item}->{$item}->{socket2}->{spi})) { $extrahit += $char->{gear}->{item}->{$item}->{socket2}->{spi}; }
	if (exists($char->{gear}->{item}->{$item}->{socket3}->{spi})) { $extrahit += $char->{gear}->{item}->{$item}->{socket3}->{spi}; }
	if (exists($char->{gear}->{item}->{$item}->{socketbonus}->{spi})) { $extrahit += $char->{gear}->{item}->{$item}->{socketbonus}->{spi}; }
	if (exists($char->{gear}->{item}->{$item}->{enchant}->{hitrtng})) { $extrahit += $char->{gear}->{item}->{$item}->{enchant}->{hitrtng}; }
	if (exists($char->{gear}->{item}->{$item}->{socket1}->{hitrtng})) { $extrahit += $char->{gear}->{item}->{$item}->{socket1}->{hitrtng}; }
	if (exists($char->{gear}->{item}->{$item}->{socket2}->{hitrtng})) { $extrahit += $char->{gear}->{item}->{$item}->{socket2}->{hitrtng}; }
	if (exists($char->{gear}->{item}->{$item}->{socket3}->{hitrtng})) { $extrahit += $char->{gear}->{item}->{$item}->{socket3}->{hitrtng}; }
	if (exists($char->{gear}->{item}->{$item}->{socketbonus}->{hitrtng})) { $extrahit += $char->{gear}->{item}->{$item}->{socketbonus}->{hitrtng}; }
	#print "extrahit: $extrahit\n";
}
print "\nextrahit: $extrahit\n";


#$extrahit = 285; # amount of hit from gems/enchants, socketboni etc
#$extrahit = 0;
if ($char->{race} eq "Draenei") {
	$hitcap = 1640;#-$extrahit;
} else {
	$hitcap = 1742;#-$extrahit;	# non draenei
}
print "effective hitcap: $hitcap\n";

@stats = ("sta", "int", "spi", "hastertng", "critstrkrtng", "hitrtng", "mastrtng", "splpwr");
@reforgestats = ("spi", "critstrkrtng", "hastertng", "hitrtng", "mastrtng");
@nonhitstats = ("critstrkrtng", "hastertng", "mastrtng");
$dep = { int => 1.55, spi => 1.94, critstrkrtng => 0.54, hastertng => 0.96, hitrtng => 1.94, mastrtng => 0.76 }; # dep values
$miss = 0;
sub calcWorth {
	$stats = { spi => 0, critstrkrtng => 0, hastertng => 0, hitrtng => 0, mastrtng => 0, int => 0 };
	foreach $item (keys(%{$char->{gear}->{item}})) {
		foreach $stat (keys(%{$stats})) {
			if (defined($char->{gear}->{item}->{$item}->{$stat})) { $stats->{$stat} += $char->{gear}->{item}->{$item}->{$stat}; }
			if (defined($char->{gear}->{item}->{$item}->{enchant}->{$stat})) { $stats->{$stat} += $char->{gear}->{item}->{$item}->{enchant}->{$stat}; }
			if (defined($char->{gear}->{item}->{$item}->{socket1}->{$stat})) { $stats->{$stat} += $char->{gear}->{item}->{$item}->{socket1}->{$stat}; }
			if (defined($char->{gear}->{item}->{$item}->{socket2}->{$stat})) { $stats->{$stat} += $char->{gear}->{item}->{$item}->{socket2}->{$stat}; }
			if (defined($char->{gear}->{item}->{$item}->{socket3}->{$stat})) { $stats->{$stat} += $char->{gear}->{item}->{$item}->{socket3}->{$stat}; }
			if (defined($char->{gear}->{item}->{$item}->{socketbonus}->{$stat})) { $stats->{$stat} += $char->{gear}->{item}->{$item}->{socketbonus}->{$stat}; }
			if (defined($char->{gear}->{item}->{$item}->{reforge}->{$stat})) { $stats->{$stat} += $char->{gear}->{item}->{$item}->{reforge}->{$stat}; }
		}
	}
	if ($stats->{hitrtng} + $stats->{spi} > $hitcap) {
		$effhitspi = $hitcap;
		$miss = 0;
	} else {
		$effhitspi = $stats->{hitrtng} + $stats->{spi};
		$miss = $hitcap - $effhitspi;
	}
	#$effmastery = int($stats->{mastrtng} / 89.64) * 89.64;
	#print Dumper($stats);
	$worth = $stats->{int} * $dep->{int} + $stats->{critstrkrtng} * $dep->{critstrkrtng} + $stats->{hastertng} * $dep->{hastertng} + $stats->{mastrtng} * $dep->{mastrtng} + $effhitspi * $dep->{hitrtng} - $miss * 10;
	#foreach $stat (keys(%{$stats})) { $char->{stats}->{$stat} = $stats->{$stat}; }
	return $worth;
}

sub getHighestNonhitTarget {
	$id = $_[0];
	$tdep = 0;
	$highTarget = "";
	foreach $stat (@nonhitstats) {
		if ((!defined($char->{gear}->{item}->{$id}->{$stat})) && ($dep->{$stat} > $tdep)) {
			$tdep = $dep->{$stat};
			$highTarget = $stat;
		}
	}
	# print "Highest target on $id is $highTarget\n";
	return $highTarget;
}

sub getLowestNonhitSource {
	$id = $_[0];
	$tdep = 100;
	$lowSource = "";
	foreach $stat (@nonhitstats) {
		# print "checking $stat\n";
		# print Dumper($char->{gear}->{item}->{$id});
		if ((defined($char->{gear}->{item}->{$id}->{$stat})) && ($dep->{$stat} < $tdep) && ($char->{gear}->{item}->{$id}->{$stat} > 0)) {
			$tdep = $dep->{$stat};
			$lowSource = $stat;
		}
	}
	# print "Lowest source on $id is $lowSource\n";
	return $lowSource;
}

sub hasHitOrSpi {
	$id = $_[0];
	$hitOrSpi = 0;
	foreach $stat ("hitrtng", "spi") {
		if ((defined($char->{gear}->{item}->{$id}->{$stat})) && ($char->{gear}->{item}->{$id}->{$stat} > 0)) {
			$hitOrSpi = $stat;
		}
	}
	return $hitOrSpi;
}

sub hasHit {
	$id = $_[0];
	$hasHit = 0;
	if (hasHitOrSpi($id) eq "hitrtng") { $hasHit = 1; }
	return $hasHit;
}

sub hasSpi {
	$id = $_[0];
	$hasSpi = 0;
	if (hasHitOrSpi($id) eq "spi") { $hasSpi = 1; }
	return $hasSpi;
}

@itemkeys = keys(%{$char->{gear}->{item}});

$maxref->{worth} = calcWorth();
$maxref->{state} = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
print "missing hit: $miss\n";
print "base worth: $maxref->{worth}\n";
$preforges = {};

#foreach $item (keys(%{$char->{gear}->{item}

#get possible reforges:
print "Building set of possible reforging combinations\n";
foreach $item (keys(%{$char->{gear}->{item}})) {
	# @sources = ();
	# @targets = ();
	# $spihit = 0;
	# foreach $stat (@reforgestats) {
		# if ((defined($char->{gear}->{item}->{$item}->{$stat})) && ($char->{gear}->{item}->{$item}->{$stat} > 0)) {
			# push(@sources, $stat);
		# } else {
			# if ((($stat eq "spi") || ($stat eq "hitrtng")) && ($spihit == 0)) {
				# push(@targets, $stat);
				# $spihit = 1;
			# } elsif (($stat ne "spi") && ($stat ne "hitrtng")) {
				# push(@targets, $stat);
			# }
		# }
	# }
	# $n = 1;
	# foreach $source (@sources) {
		# foreach $target (@targets) {
			# if ( (($miss > 0) && not(($source eq "spi") && ($target eq "hitrtng")) && not(($source eq "hitrtng") && ($target eq "spi")) && (($source eq "hitrtng") || ($source eq "spi") || ($dep->{$target} > $dep->{$source})))
				# || (($miss == 0) && ($target ne "spi") && ($target ne "hitrtng") && (($source eq "hitrtng") || ($source eq "spi") || ($dep->{$target} > $dep->{$source}))) )
			# {
				# $preforges->{$item}[$n]->{source} = $source;
				# $preforges->{$item}[$n]->{target} = $target;
				# $n++;
			# }
		# }
	# }
	# if ($#{$sources} == -1) {
		# $preforges->{$item}[0]->{source} = "";
		# $preforges->{$item}[0]->{target} = "";
	# }
	
	$preforges->{$item} = [];
	$lowestsource = getLowestNonhitSource($item);
	$highesttarget = getHighestNonhitTarget($item);
	$hasHit = hasHit($item);
	$hasSpi = hasSpi($item);
	#print $hasHit;
	if ($hasHit) {
		if ($miss == 0) {
			if ($highesttarget ne "") {
				$n = $#{$preforges->{$item}}+1;
				$preforges->{$item}[$n]->{source} = "hitrtng";
				$preforges->{$item}[$n]->{target} = $highesttarget;
			}
		}
	} elsif ($hasSpi) {
		if ($miss == 0){
			if ($highesttarget ne "") {
				$n = $#{$preforges->{$item}}+1;
				$preforges->{$item}[$n]->{source} = "spi";
				$preforges->{$item}[$n]->{target} = $highesttarget;
			}
		} else {
			if ($lowestsource ne "") {
				$n = $#{$preforges->{$item}}+1;
				$preforges->{$item}[$n]->{source} = $lowestsource;
				$preforges->{$item}[$n]->{target} = "hitrtng";
			}
		}
	} else {
		if ($miss > 0) {
			if ($lowestsource ne "") {
				$n = $#{$preforges->{$item}}+1;
				$preforges->{$item}[$n]->{source} = $lowestsource;
				$preforges->{$item}[$n]->{target} = "spi";
			}
		}
	}
	if (($lowestsource ne $highesttarget) && ($dep->{$highesttarget} > $dep->{$lowestsource})) {
		if (($highesttarget ne "") && ($lowestsource ne "")) {
			$n = $#{$preforges->{$item}}+1;
			$preforges->{$item}[$n]->{source} = $lowestsource;
			$preforges->{$item}[$n]->{target} = $highesttarget;
		}
	} else {
		$n = $#{$preforges->{$item}}+1;
		$preforges->{$item}[$n]->{source} = "";
		$preforges->{$item}[$n]->{target} = "";
	}
	if ($#{$preforges->{$item}} == -1) {
		$preforges->{$item}[0]->{source} = "";
		$preforges->{$item}[0]->{target} = "";
	}
	#print "$item\n";
	#print Dumper($preforges->{$item});
}

$combinations = 1;
foreach $item (keys(%{$char->{gear}->{item}})) {
	#$r = 0;
	foreach $ref (@{$preforges->{$item}}) {
		if ($ref->{source} ne "") {
			print "$item: $statnames{$ref->{source}} to $statnames{$ref->{target}}\n";
		} else {
			print "$item: Don't reforge\n";
		}
		#$r++;
	}
	$combinations = $combinations * ($#{$preforges->{$item}}+1);
}
print "$combinations possible combinations\n";
foreach $item (keys(%{$char->{gear}->{item}})) {
	$numref->{$item} = $#{$preforges->{$item}};
}
for ($n=0;$n<=$#itemkeys;$n++) {
	$state[$n] = 0;
	$maxstate[$n] = int($#{$preforges->{$itemkeys[$n]}});
}

#print Dumper($stats);
#print "state: " . Dumper(\@state);
#print "maxstate: " . Dumper(\@maxstate);
$m = 0;
for ($state[0]=0;$state[0]<=$maxstate[0];$state[0]++) {
for ($state[1]=0;$state[1]<=$maxstate[1];$state[1]++) {
for ($state[2]=0;$state[2]<=$maxstate[2];$state[2]++) {
for ($state[3]=0;$state[3]<=$maxstate[3];$state[3]++) {
for ($state[4]=0;$state[4]<=$maxstate[4];$state[4]++) {
for ($state[5]=0;$state[5]<=$maxstate[5];$state[5]++) {
for ($state[6]=0;$state[6]<=$maxstate[6];$state[6]++) {
for ($state[7]=0;$state[7]<=$maxstate[7];$state[7]++) {
for ($state[8]=0;$state[8]<=$maxstate[8];$state[8]++) {
for ($state[9]=0;$state[9]<=$maxstate[9];$state[9]++) {
for ($state[10]=0;$state[10]<=$maxstate[10];$state[10]++) {
for ($state[11]=0;$state[11]<=$maxstate[11];$state[11]++) {
for ($state[12]=0;$state[12]<=$maxstate[12];$state[12]++) {
for ($state[13]=0;$state[13]<=$maxstate[13];$state[13]++) {
for ($state[14]=0;$state[14]<=$maxstate[14];$state[14]++) {
for ($state[15]=0;$state[15]<=$maxstate[15];$state[15]++) {
for ($state[16]=0;$state[16]<=$maxstate[16];$state[16]++) {
	$m++;
	for ($i=0;$i<=16;$i++) {
		if ($preforges->{$itemkeys[$i]}[$state[$i]]->{source} ne "") {
			$amount[$i] = int(0.4 * $char->{gear}->{item}->{$itemkeys[$i]}->{$preforges->{$itemkeys[$i]}[$state[$i]]->{source}});
			$char->{gear}->{item}->{$itemkeys[$i]}->{reforge}->{$preforges->{$itemkeys[$i]}[$state[$i]]->{source}} = - $amount[$i];
			$char->{gear}->{item}->{$itemkeys[$i]}->{reforge}->{$preforges->{$itemkeys[$i]}[$state[$i]]->{target}} = $amount[$i];
		}
	}
	$newworth = calcWorth();
	if ($newworth > $maxref->{worth}) {
		@curstate = @state;
		$maxref->{worth} = $newworth;
		$maxref->{state} = \@curstate;
		print sprintf("new max worth: %.4f (%.8f%%)\n", $newworth, 100*$m/$combinations);
		#print Dumper($stats);
		# for ($i=0;$i<=16;$i++) {
			# if ($preforges->{$itemkeys[$i]}[$maxref->{state}->[$i]]->{source} ne "") {
				# print "$itemkeys[$i]: $statnames{$preforges->{$itemkeys[$i]}[$maxref->{state}->[$i]]->{source}} to $statnames{$preforges->{$itemkeys[$i]}[$maxref->{state}->[$i]]->{target}}\n";
			# } else {
				# print "$itemkeys[$i]: Don't reforge\n";
			# }
		# }
	}
	for ($i=0;$i<=16;$i++) {
		if ($preforges->{$itemkeys[$i]}[$state[$i]]->{source} ne "") {
			delete $char->{gear}->{item}->{$itemkeys[$i]}->{reforge};
			# $char->{gear}->{item}->{$itemkeys[$i]}->{$preforges->{$itemkeys[$i]}[$state[$i]]->{source}} += $amount[$i];
			# $char->{gear}->{item}->{$itemkeys[$i]}->{$preforges->{$itemkeys[$i]}[$state[$i]]->{target}} -= $amount[$i];
		}
	}
}}}}}}}}}}}}}}}}}

print "- - - - - - - - - - -\n";
print Dumper($char->{stats});
for ($i=0;$i<=16;$i++) {
	if ($preforges->{$itemkeys[$i]}[$maxref->{state}->[$i]]->{source} ne "") {
		print "$itemkeys[$i]: $statnames{$preforges->{$itemkeys[$i]}[$maxref->{state}->[$i]]->{source}} to $statnames{$preforges->{$itemkeys[$i]}[$maxref->{state}->[$i]]->{target}}\n";
		$amount = int($char->{gear}->{item}->{$itemkeys[$i]}->{$preforges->{$itemkeys[$i]}[$maxref->{state}->[$i]]->{source}} * 0.4);
		$char->{gear}->{item}->{$itemkeys[$i]}->{reforge}->{$preforges->{$itemkeys[$i]}[$maxref->{state}->[$i]]->{source}} = - $amount;
		$char->{gear}->{item}->{$itemkeys[$i]}->{reforge}->{$preforges->{$itemkeys[$i]}[$maxref->{state}->[$i]]->{target}} =  $amount;
	} else {
		delete $char->{gear}->{item}->{$itemkeys[$i]}->{reforge};
		print "$itemkeys[$i]: Don't reforge\n";
	}
}

sub saveToFile {
	$xmlout = $_[0];
	$outfile = $_[1];
	open(SAVE, ">:utf8", "$outfile")
		or return -1;
	print SAVE XMLout($xmlout, KeyAttr => {item => 'name'});
	close(SAVE);
}

foreach $stat (@stats) { $char->{stats}->{$stat} = 0; }
foreach $item (keys(%{$char->{gear}->{item}})) {
	foreach $stat (@stats) {
		if (defined($char->{gear}->{item}->{$item}->{$stat})) { $char->{stats}->{$stat} += $char->{gear}->{item}->{$item}->{$stat}; }
		if (defined($char->{gear}->{item}->{$item}->{enchant}->{$stat})) { $char->{stats}->{$stat} += $char->{gear}->{item}->{$item}->{enchant}->{$stat}; }
		if (defined($char->{gear}->{item}->{$item}->{reforge}->{$stat})) { $char->{stats}->{$stat} += $char->{gear}->{item}->{$item}->{reforge}->{$stat}; }
		if (defined($char->{gear}->{item}->{$item}->{socket1}->{$stat})) { $char->{stats}->{$stat} += $char->{gear}->{item}->{$item}->{socket1}->{$stat}; }
		if (defined($char->{gear}->{item}->{$item}->{socket2}->{$stat})) { $char->{stats}->{$stat} += $char->{gear}->{item}->{$item}->{socket2}->{$stat}; }
		if (defined($char->{gear}->{item}->{$item}->{socket3}->{$stat})) { $char->{stats}->{$stat} += $char->{gear}->{item}->{$item}->{socket3}->{$stat}; }
		if (defined($char->{gear}->{item}->{$item}->{socketbonus}->{$stat})) { $char->{stats}->{$stat} += $char->{gear}->{item}->{$item}->{socketbonus}->{$stat}; }
		if (defined($char->{gear}->{item}->{$item}->{tinker}->{$stat})) { $char->{stats}->{$stat} += $char->{gear}->{item}->{$item}->{tinker}->{$stat}; }
	}
	#check for nonmail-gear
	if ($char->{gear}->{item}->{$item}->{armorclass} =~ m/1|2|4/) { $mailspec = 0; }
}
#add buffs
#bufffood (90int) and flask (300int)
$char->{stats}->{int} += 390;
#motw/bok
$char->{stats}->{sta} = $char->{stats}->{sta} * 1.05;
$char->{stats}->{int} = $char->{stats}->{int} * 1.05;
#mailspec
$char->{stats}->{int} = $char->{stats}->{int} * (1 + $mailspec * 0.05);
#int -> spellpower
$char->{stats}->{splpwr} += $char->{stats}->{int};
#+10% spellpower
$char->{stats}->{splpwr} = $char->{stats}->{splpwr} * 1.1;
#mana
$char->{stats}->{mana} = 23430 + ($char->{stats}->{int} - 20) * 15 + 20 + 2126;
print Dumper($char->{stats});

if (($ARGV[1] eq "save") && (exists($ARGV[2]))) { saveToFile($char, $ARGV[2]); }